"""
数据缓存管理器

提供智能的数据缓存机制，避免重复调用AkShare接口
"""

import time
import threading
from datetime import datetime, timedelta
from typing import Dict, Any, Optional, Callable, Tuple
import logging
import hashlib
import json
from dataclasses import dataclass, asdict
from enum import Enum

logger = logging.getLogger(__name__)


class CacheLevel(Enum):
    """缓存级别"""
    SESSION = "session"      # 会话级缓存（程序运行期间）
    SHORT = "short"         # 短期缓存（5分钟）
    MEDIUM = "medium"       # 中期缓存（30分钟）
    LONG = "long"          # 长期缓存（2小时）


@dataclass
class CacheEntry:
    """缓存条目"""
    data: Any
    timestamp: float
    ttl: float  # 生存时间（秒）
    access_count: int = 0
    last_access: float = 0
    
    def is_expired(self) -> bool:
        """检查是否过期"""
        return time.time() - self.timestamp > self.ttl
    
    def access(self) -> Any:
        """访问数据，更新访问统计"""
        self.access_count += 1
        self.last_access = time.time()
        return self.data


class DataCacheManager:
    """数据缓存管理器"""
    
    # 缓存TTL配置（秒）
    CACHE_TTL = {
        CacheLevel.SESSION: float('inf'),  # 永不过期（直到程序结束）
        CacheLevel.SHORT: 300,             # 5分钟
        CacheLevel.MEDIUM: 1800,           # 30分钟
        CacheLevel.LONG: 7200              # 2小时
    }
    
    def __init__(self):
        self._cache: Dict[str, CacheEntry] = {}
        self._lock = threading.RLock()
        self._stats = {
            "hits": 0,
            "misses": 0,
            "evictions": 0
        }
        
    def _generate_key(self, func_name: str, *args, **kwargs) -> str:
        """生成缓存键"""
        # 创建参数的哈希值
        params = {
            "args": args,
            "kwargs": kwargs
        }
        param_str = json.dumps(params, sort_keys=True, default=str)
        param_hash = hashlib.md5(param_str.encode()).hexdigest()[:8]
        
        return f"{func_name}:{param_hash}"
    
    def get(self, key: str) -> Optional[Any]:
        """获取缓存数据"""
        with self._lock:
            if key in self._cache:
                entry = self._cache[key]
                if not entry.is_expired():
                    self._stats["hits"] += 1
                    logger.debug(f"缓存命中: {key}")
                    return entry.access()
                else:
                    # 过期数据，删除
                    del self._cache[key]
                    self._stats["evictions"] += 1
                    logger.debug(f"缓存过期删除: {key}")
            
            self._stats["misses"] += 1
            return None
    
    def set(self, key: str, data: Any, cache_level: CacheLevel = CacheLevel.MEDIUM):
        """设置缓存数据"""
        with self._lock:
            ttl = self.CACHE_TTL[cache_level]
            entry = CacheEntry(
                data=data,
                timestamp=time.time(),
                ttl=ttl
            )
            self._cache[key] = entry
            logger.debug(f"缓存设置: {key} (TTL: {ttl}s)")
    
    def cached_call(self, func: Callable, cache_level: CacheLevel = CacheLevel.MEDIUM, 
                   *args, **kwargs) -> Any:
        """缓存装饰器调用"""
        key = self._generate_key(func.__name__, *args, **kwargs)
        
        # 尝试从缓存获取
        cached_data = self.get(key)
        if cached_data is not None:
            return cached_data
        
        # 缓存未命中，执行函数
        logger.debug(f"执行函数: {func.__name__}")
        start_time = time.time()
        
        try:
            result = func(*args, **kwargs)
            execution_time = time.time() - start_time
            
            # 存储到缓存
            self.set(key, result, cache_level)
            
            logger.info(f"函数执行完成: {func.__name__} ({execution_time:.2f}s)")
            return result
            
        except Exception as e:
            logger.error(f"函数执行失败: {func.__name__} - {e}")
            raise
    
    def invalidate(self, pattern: str = None):
        """清除缓存"""
        with self._lock:
            if pattern is None:
                # 清除所有缓存
                count = len(self._cache)
                self._cache.clear()
                logger.info(f"清除所有缓存: {count} 项")
            else:
                # 清除匹配模式的缓存
                keys_to_remove = [key for key in self._cache.keys() if pattern in key]
                for key in keys_to_remove:
                    del self._cache[key]
                logger.info(f"清除匹配缓存: {len(keys_to_remove)} 项 (模式: {pattern})")
    
    def cleanup_expired(self):
        """清理过期缓存"""
        with self._lock:
            expired_keys = [
                key for key, entry in self._cache.items() 
                if entry.is_expired()
            ]
            
            for key in expired_keys:
                del self._cache[key]
                self._stats["evictions"] += 1
            
            if expired_keys:
                logger.info(f"清理过期缓存: {len(expired_keys)} 项")
    
    def get_stats(self) -> Dict[str, Any]:
        """获取缓存统计"""
        with self._lock:
            total_requests = self._stats["hits"] + self._stats["misses"]
            hit_rate = (self._stats["hits"] / total_requests * 100) if total_requests > 0 else 0
            
            return {
                "cache_size": len(self._cache),
                "hits": self._stats["hits"],
                "misses": self._stats["misses"],
                "hit_rate": round(hit_rate, 2),
                "evictions": self._stats["evictions"],
                "entries": [
                    {
                        "key": key,
                        "size": len(str(entry.data)) if entry.data else 0,
                        "age": round(time.time() - entry.timestamp, 2),
                        "ttl": entry.ttl,
                        "access_count": entry.access_count,
                        "expired": entry.is_expired()
                    }
                    for key, entry in self._cache.items()
                ]
            }
    
    def print_stats(self):
        """打印缓存统计"""
        stats = self.get_stats()
        
        print("\n📊 数据缓存统计")
        print("-" * 40)
        print(f"缓存大小: {stats['cache_size']} 项")
        print(f"命中次数: {stats['hits']}")
        print(f"未命中次数: {stats['misses']}")
        print(f"命中率: {stats['hit_rate']}%")
        print(f"清理次数: {stats['evictions']}")
        
        if stats['entries']:
            print(f"\n缓存条目:")
            for entry in stats['entries'][:5]:  # 只显示前5个
                status = "❌过期" if entry['expired'] else "✅有效"
                print(f"  {entry['key'][:30]}... - {status} (访问{entry['access_count']}次)")


# 全局缓存管理器实例
_global_cache_manager = None
_cache_lock = threading.Lock()


def get_cache_manager() -> DataCacheManager:
    """获取全局缓存管理器实例"""
    global _global_cache_manager
    
    if _global_cache_manager is None:
        with _cache_lock:
            if _global_cache_manager is None:
                _global_cache_manager = DataCacheManager()
                logger.info("数据缓存管理器初始化完成")
    
    return _global_cache_manager


def cached(cache_level: CacheLevel = CacheLevel.MEDIUM):
    """缓存装饰器"""
    def decorator(func: Callable):
        def wrapper(*args, **kwargs):
            cache_manager = get_cache_manager()
            return cache_manager.cached_call(func, cache_level, *args, **kwargs)
        
        wrapper.__name__ = func.__name__
        wrapper.__doc__ = func.__doc__
        return wrapper
    
    return decorator


def invalidate_cache(pattern: str = None):
    """清除缓存的便捷函数"""
    cache_manager = get_cache_manager()
    cache_manager.invalidate(pattern)


def get_cache_stats() -> Dict[str, Any]:
    """获取缓存统计的便捷函数"""
    cache_manager = get_cache_manager()
    return cache_manager.get_stats()


def print_cache_stats():
    """打印缓存统计的便捷函数"""
    cache_manager = get_cache_manager()
    cache_manager.print_stats()


# 定期清理过期缓存的后台任务
def start_cache_cleanup_task():
    """启动缓存清理后台任务"""
    import threading
    import time
    
    def cleanup_worker():
        while True:
            try:
                time.sleep(300)  # 每5分钟清理一次
                cache_manager = get_cache_manager()
                cache_manager.cleanup_expired()
            except Exception as e:
                logger.error(f"缓存清理任务异常: {e}")
    
    cleanup_thread = threading.Thread(target=cleanup_worker, daemon=True)
    cleanup_thread.start()
    logger.info("缓存清理后台任务已启动")


# 自动启动清理任务
start_cache_cleanup_task()
